package cn.topcodes.tcsf.wechat.filter;

import cn.topcode.unicorn.utils.StringUtil;
import cn.topcode.unicorn.wxsdk.WXContext;
import cn.topcode.unicorn.wxsdk.WechatMpDataSource;
import cn.topcode.unicorn.wxsdk.base.WXMessageCenter;
import cn.topcode.unicorn.wxsdk.message.receive.event.*;
import cn.topcode.unicorn.wxsdk.message.receive.msg.*;
import cn.topcodes.tcsf.wechat.event.TemplateSendFinishEvent;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;

@Slf4j
public class WXFilter implements Filter {
    private static final String MEDIA_ID = "MediaId";
    private static final String EVENT_KEY = "EventKey";
    private WechatMpDataSource wechatMpDataSource;


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        process((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
    }

    @Override
    public void destroy() {
    }

    protected void process(HttpServletRequest request, HttpServletResponse response) {
        try {
            OutputStream out = response.getOutputStream();
            if ("GET".equalsIgnoreCase(request.getMethod())) {
                String signature = request.getParameter("signature");
                String timestamp = request.getParameter("timestamp");
                String nonce = request.getParameter("nonce");
                String echostr = request.getParameter("echostr");
                if (StringUtil.isNotBlank(signature) && StringUtil.isNotBlank(timestamp) && StringUtil.isNotBlank(nonce) && StringUtil.isNotBlank(echostr)) {
                    out.write(echostr.getBytes());
                }

                return;
            }

            SAXReader reader = new SAXReader();
            reader.setEncoding("utf-8");
            Document document = reader.read(request.getInputStream());
            document.setXMLEncoding("utf-8");
            Element root = document.getRootElement();
            Element msgTypeElement = root.element("MsgType");
            String msgType = msgTypeElement.getTextTrim();
            switch (msgType) {
                case "link":
                    this.handleLinkMessage(root, msgType, out);
                    break;
                case "text":
                    this.handleTextMessage(root, msgType, out);
                    break;
                case "event":
                    this.handleEvent(root, msgType, out);
                    break;
                case "image":
                    this.handleImageMessage(root, msgType, out);
                    break;
                case "video":
                    this.handleVideoMessage(root, msgType, out);
                    break;
                case "voice":
                    this.handleVoiceMessage(root, msgType, out);
                    break;
                case "shortvideo":
                    this.handleShortVideoMessage(root, msgType, out);
                    break;
                case "location":
                    this.handleLocationMessage(root, msgType, out);
                    break;
            }
        } catch (Exception ex) {
            log.error("微信回调消息处理失败, ", ex);
        }
    }

    protected void handleEvent(Element root, String msgType, OutputStream out) {
        Element eventElement = root.element("Event");
        String event = eventElement.getTextTrim();
        switch (event) {
            case "LOCATION":
                this.handleLocationEvent(root, msgType, event, out);
                break;
            case "SCAN":
                this.handleScanQrcodeEvent(root, msgType, event, out);
                break;
            case "VIEW":
                this.handleClickLinkMenuEvent(root, msgType, event, out);
                break;
            case "location_select":
                break;
            case "CLICK":
                this.handleClickMessageMenuEvent(root, msgType, event, out);
                break;
            case "subscribe":
            case "unsubscribe":
                this.handleSubscribeEvent(root, msgType, event, out);
                break;
            case "scancode_push":
                this.handleScanCodePush(root, msgType, event, out);
                break;
            case "pic_weixin":
                this.handlePicWeixin(root, msgType, event, out);
                break;
            case "pic_photo_or_album":
                this.handlePicPhotoOrAlbum(root, msgType, event, out);
                break;
            case "pic_sysphoto":
                this.handlePicSysPhoto(root, msgType, event, out);
                break;
            case "scancode_waitmsg":
                this.handleScanCodeWaitMsg(root, msgType, event, out);
                break;
            // 模版消息发送回调
            case "TEMPLATESENDJOBFINISH":
                this.handleTemplateSendFinishEvent(root, msgType, event, out);
                break;
        }
    }

    protected void handlePicWeixin(Element root, String msgType, String event, OutputStream out) {
        ClickLinkMenuEvent clickLinkMenuEvent = new ClickLinkMenuEvent();
        this.fillBasicProperty(root, clickLinkMenuEvent);
        clickLinkMenuEvent.setMsgType(msgType);
        clickLinkMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickLinkMenuEvent.setEventKey(eventKey);
        this.broadcastClickLinkMenuEvent(clickLinkMenuEvent, out);
    }

    protected void handlePicPhotoOrAlbum(Element root, String msgType, String event, OutputStream out) {
        ClickLinkMenuEvent clickLinkMenuEvent = new ClickLinkMenuEvent();
        this.fillBasicProperty(root, clickLinkMenuEvent);
        clickLinkMenuEvent.setMsgType(msgType);
        clickLinkMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickLinkMenuEvent.setEventKey(eventKey);
        this.broadcastClickLinkMenuEvent(clickLinkMenuEvent, out);
    }

    protected void handlePicSysPhoto(Element root, String msgType, String event, OutputStream out) {
        ClickLinkMenuEvent clickLinkMenuEvent = new ClickLinkMenuEvent();
        this.fillBasicProperty(root, clickLinkMenuEvent);
        clickLinkMenuEvent.setMsgType(msgType);
        clickLinkMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickLinkMenuEvent.setEventKey(eventKey);
        this.broadcastClickLinkMenuEvent(clickLinkMenuEvent, out);
    }

    protected void handleScanCodeWaitMsg(Element root, String msgType, String event, OutputStream out) {
        ScanCodeWaitMsgEvent scanCodeWaitMsgEvent = new ScanCodeWaitMsgEvent();
        this.fillBasicProperty(root, scanCodeWaitMsgEvent);
        scanCodeWaitMsgEvent.setMsgType(msgType);
        scanCodeWaitMsgEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        scanCodeWaitMsgEvent.setEventKey(eventKey);
        Element scanCodeInfo = root.element("ScanCodeInfo");
        Element scanType = scanCodeInfo.element("ScanType");
        Element scanResult = scanCodeInfo.element("ScanResult");
        scanCodeWaitMsgEvent.setScanType(scanType.getTextTrim());
        scanCodeWaitMsgEvent.setScanResult(scanResult.getTextTrim());
        this.broadcastScanCodeWaitMsgEvent(scanCodeWaitMsgEvent, out);
    }

    protected void broadcastScanCodeWaitMsgEvent(ScanCodeWaitMsgEvent scanCodeWaitMsgEvent, OutputStream out) {
        WXMessageCenter.getInstance().publish(scanCodeWaitMsgEvent, out);
    }

    protected void handleScanCodePush(Element root, String msgType, String event, OutputStream out) {
        ClickLinkMenuEvent clickLinkMenuEvent = new ClickLinkMenuEvent();
        this.fillBasicProperty(root, clickLinkMenuEvent);
        clickLinkMenuEvent.setMsgType(msgType);
        clickLinkMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickLinkMenuEvent.setEventKey(eventKey);
        this.broadcastClickLinkMenuEvent(clickLinkMenuEvent, out);
    }

    protected void handleClickLinkMenuEvent(Element root, String msgType, String event, OutputStream out) {
        ClickLinkMenuEvent clickLinkMenuEvent = new ClickLinkMenuEvent();
        this.fillBasicProperty(root, clickLinkMenuEvent);
        clickLinkMenuEvent.setMsgType(msgType);
        clickLinkMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickLinkMenuEvent.setEventKey(eventKey);
        this.broadcastClickLinkMenuEvent(clickLinkMenuEvent, out);
    }

    protected void handleScanQrcodeEvent(Element root, String msgType, String event, OutputStream out) {
        ScanQrcodeEvent scanQrcodeEvent = new ScanQrcodeEvent();
        this.fillBasicProperty(root, scanQrcodeEvent);
        scanQrcodeEvent.setMsgType(msgType);
        scanQrcodeEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        scanQrcodeEvent.setEventKey(eventKey);
        Element ticketElement = root.element("Ticket");
        String ticket = ticketElement.getTextTrim();
        scanQrcodeEvent.setTicket(ticket);
        WXMessageCenter.getInstance().publish(scanQrcodeEvent, out);
    }

    protected void handleLocationEvent(Element root, String msgType, String event, OutputStream out) {
        LocationEvent locationEvent = new LocationEvent();
        this.fillBasicProperty(root, locationEvent);
        locationEvent.setMsgType(msgType);
        locationEvent.setEvent(event);
        Element latitudeElement = root.element("Latitude");
        double latitude = Double.parseDouble(latitudeElement.getTextTrim());
        locationEvent.setLatitude(latitude);
        Element longitudeElement = root.element("Longitude");
        double longitude = Double.parseDouble(longitudeElement.getTextTrim());
        locationEvent.setLongitude(longitude);
        Element precisionElement = root.element("Precision");
        double precision = Double.parseDouble(precisionElement.getTextTrim());
        locationEvent.setPrecision(precision);
        WXMessageCenter.getInstance().publish(locationEvent, out);
    }

    protected void handleClickMessageMenuEvent(Element root, String msgType, String event, OutputStream out) {
        ClickMessageMenuEvent clickMessageMenuEvent = new ClickMessageMenuEvent();
        this.fillBasicProperty(root, clickMessageMenuEvent);
        clickMessageMenuEvent.setMsgType(msgType);
        clickMessageMenuEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        String eventKey = eventKeyElement.getTextTrim();
        clickMessageMenuEvent.setEventKey(eventKey);
        WXMessageCenter.getInstance().publish(clickMessageMenuEvent, out);
    }

    protected void handleSubscribeEvent(Element root, String msgType, String event, OutputStream out) {
        SubscribeEvent subscribeEvent = new SubscribeEvent();
        this.fillBasicProperty(root, subscribeEvent);
        subscribeEvent.setMsgType(msgType);
        subscribeEvent.setEvent(event);
        Element eventKeyElement = root.element("EventKey");
        if (eventKeyElement != null) {
            String eventKey = eventKeyElement.getTextTrim();
            subscribeEvent.setEventKey(eventKey);
        }

        Element ticketElement = root.element("Ticket");
        if (ticketElement != null) {
            String ticket = ticketElement.getTextTrim();
            subscribeEvent.setTicket(ticket);
        }

        WXMessageCenter.getInstance().publish(subscribeEvent, out);
    }

    protected void handleTemplateSendFinishEvent(Element root, String msgType, String event, OutputStream out) {
        TemplateSendFinishEvent sendFinishEvent = new TemplateSendFinishEvent();
        this.fillBasicProperty(root, sendFinishEvent);
        sendFinishEvent.setMsgType(msgType);
        sendFinishEvent.setEvent(event);
        Element msgIdElement = root.element("MsgID");
        if (msgIdElement != null) {
            String msgId = msgIdElement.getTextTrim();
            sendFinishEvent.setMsgId(msgId);
        }

        Element statusElement = root.element("Status");
        if (statusElement != null) {
            String status = statusElement.getTextTrim();
            sendFinishEvent.setStatus(status);
        }

        WXMessageCenter.getInstance().publish(sendFinishEvent, out);
    }

    protected void handleVoiceMessage(Element root, String msgType, OutputStream out) {
        VoiceMessage voiceMessage = new VoiceMessage();
        this.fillBasicProperty(root, voiceMessage);
        voiceMessage.setMsgType(msgType);
        Element formatElement = root.element("Format");
        String format = formatElement.getTextTrim();
        voiceMessage.setFormat(format);
        Element mediaIdElement = root.element("MediaId");
        String mediaId = mediaIdElement.getTextTrim();
        voiceMessage.setMediaId(mediaId);
        Element recognitionElement = root.element("Recognition");
        if (recognitionElement != null) {
            voiceMessage.setRecognition(recognitionElement.getTextTrim());
        }

        WXMessageCenter.getInstance().publish(voiceMessage, out);
    }

    protected void handleVideoMessage(Element root, String msgType, OutputStream out) {
        VideoMessage videoMessage = new VideoMessage();
        this.fillBasicProperty(root, videoMessage);
        videoMessage.setMsgType(msgType);
        Element mediaIdElement = root.element("MediaId");
        String mediaId = mediaIdElement.getTextTrim();
        videoMessage.setMediaId(mediaId);
        Element thumbMediaIdElement = root.element("ThumbMediaId");
        String thumbMediaId = thumbMediaIdElement.getTextTrim();
        videoMessage.setThumbMediaId(thumbMediaId);
        WXMessageCenter.getInstance().publish(videoMessage, out);
    }

    protected void handleShortVideoMessage(Element root, String msgType, OutputStream out) {
        ShortVideoMessage shortVideoMessage = new ShortVideoMessage();
        this.fillBasicProperty(root, shortVideoMessage);
        shortVideoMessage.setMsgType(msgType);
        Element mediaIdElement = root.element("MediaId");
        String mediaId = mediaIdElement.getTextTrim();
        shortVideoMessage.setMediaId(mediaId);
        Element thumbMediaIdElement = root.element("ThumbMediaId");
        String thumbMediaId = thumbMediaIdElement.getTextTrim();
        shortVideoMessage.setThumbMediaId(thumbMediaId);
        WXMessageCenter.getInstance().publish(shortVideoMessage, out);
    }

    protected void handleLocationMessage(Element root, String msgType, OutputStream out) {
        LocationMessage locationMessage = new LocationMessage();
        this.fillBasicProperty(root, locationMessage);
        locationMessage.setMsgType(msgType);
        Element labelElement = root.element("Label");
        String label = labelElement.getTextTrim();
        locationMessage.setLabel(label);
        Element locationXElement = root.element("Location_X");
        double locationX = Double.parseDouble(locationXElement.getTextTrim());
        locationMessage.setLocationX(locationX);
        Element locationYElement = root.element("Location_Y");
        double locationY = Double.parseDouble(locationYElement.getTextTrim());
        locationMessage.setLocationY(locationY);
        Element scaleElement = root.element("Scale");
        int scale = Integer.parseInt(scaleElement.getTextTrim());
        locationMessage.setScale(scale);
        WXMessageCenter.getInstance().publish(locationMessage, out);
    }

    protected void handleLinkMessage(Element root, String msgType, OutputStream out) {
        LinkMessage linkMessage = new LinkMessage();
        this.fillBasicProperty(root, linkMessage);
        linkMessage.setMsgType(msgType);
        Element titleElement = root.element("Title");
        String title = titleElement.getTextTrim();
        linkMessage.setTitle(title);
        Element urlElement = root.element("Url");
        String url = urlElement.getTextTrim();
        linkMessage.setUrl(url);
        Element descriptionElement = root.element("Description");
        String description = descriptionElement.getTextTrim();
        linkMessage.setDescription(description);
        WXMessageCenter.getInstance().publish(linkMessage, out);
    }

    protected void handleImageMessage(Element root, String msgType, OutputStream out) {
        ImageMessage imageMessage = new ImageMessage();
        this.fillBasicProperty(root, imageMessage);
        imageMessage.setMsgType(msgType);
        Element picUrlElement = root.element("PicUrl");
        String picUrl = picUrlElement.getTextTrim();
        imageMessage.setPicUrl(picUrl);
        Element mediaIdElement = root.element("MediaId");
        String mediaId = mediaIdElement.getTextTrim();
        imageMessage.setMediaId(mediaId);
        WXMessageCenter.getInstance().publish(imageMessage, out);
    }

    protected void handleTextMessage(Element root, String msgType, OutputStream out) {
        TextMessage textMessage = new TextMessage();
        this.fillBasicProperty(root, textMessage);
        textMessage.setMsgType(msgType);
        Element contentElement = root.element("Content");
        String content = contentElement.getTextTrim();
        textMessage.setContent(content);
        WXMessageCenter.getInstance().publish(textMessage, out);
    }

    protected void broadcastClickLinkMenuEvent(ClickLinkMenuEvent clickLinkMenuEvent, OutputStream out) {
        WXMessageCenter.getInstance().publish(clickLinkMenuEvent, out);
    }

    protected void fillBasicProperty(Element root, Message message) {
        Element toUserNameElement = root.element("ToUserName");
        String toUserName = toUserNameElement.getTextTrim();
        Element fromUserNameElement = root.element("FromUserName");
        String fromUserName = fromUserNameElement.getTextTrim();
        Element createTimeElement = root.element("CreateTime");
        Date createTime = new Date(Long.parseLong(createTimeElement.getTextTrim()) * 1000L);
        message.setCreateTime(createTime);
        message.setFromUserName(fromUserName);
        message.setToUserName(toUserName);
        if (message instanceof GenericMessage) {
            Element msgIdElement = root.element("MsgId");
            long msgId = Long.parseLong(msgIdElement.getTextTrim());
            ((GenericMessage) message).setMsgId(msgId);
        }
    }

    public WechatMpDataSource getWechatMpDataSource() {
        return this.wechatMpDataSource;
    }

    public void setWechatMpDataSource(WechatMpDataSource wechatMpDataSource) {
        this.wechatMpDataSource = wechatMpDataSource;
        WXContext.setWechatMpDataSource(wechatMpDataSource);
    }
}
